/**
 * 现在用的最多的是观察者模式的变体-发布-订阅模式
 简单解释下该模式，比如我们订阅了某些微信公众号，然后就等着别人发布信息，我们就能立刻接受到信息了
 */

//发布-订阅模式
class Pubsub {
  /**
   * topics:{
      inbox/newMessage: [
        {token: "1", func: ƒ},
        {token: "3", func: ƒ}
      ],
      inbox/newMessage1: [
        {token: "2", func: ƒ}
      ],
    }
   */
  constructor() {
    this.subUid = 0; //订阅的id值
    this.topics = {};//存放所有订阅者
  }

  /**
   * 发布消息
   * @param topic
   * @param args
   * @returns {*}
   */
  publish(topic, args) {
    if (!this.topics[topic]) {//发布的消息不能为空
      return false;
    }

    let subscribers = this.topics[topic];
    let len = subscribers ? subscribers.length : 0;

    while (len--) {
      subscribers[len].func(topic, args);//此处直接调用对象中的函数
    }
    return this;
  }

  /**
   * 绑定订阅
   * @param topic 订阅的消息字符串
   * @param func 订阅的消息后自定执行的方法
   * @returns {string} 返回当前的订阅的标志 token；
   */
  subscribe(topic, func) {
    if (!this.topics[topic]) {
      this.topics[topic] = [];
    }

    let token = (++this.subUid).toString();//标志唯一性的字符串，初始值为1，会自增长
    this.topics[topic].push({
      token: token,
      func: func
    });
    return token;
  }

  /**
   * 取消订阅
   * @param token 订阅消息返回的唯一标志，即 subscribe 返回的 字符串
   * @returns {*}
   */
  unsubscribe(token) {
    for (let m in this.topics) {
      if (this.topics[m]) {
        for (let i = 0, j = this.topics[m].length; i < j; i++) {
          if (this.topics[m][i].token === token) {
            this.topics[m].splice(i, 1);
            return token;
          }
        }
      }
    }
    return this;
  }
}

//例子
let messageLogger = function (topics, data) {
  console.log("Logging: " + topics + ": " + data);
};
let pubsub = new Pubsub();
let subscription = pubsub.subscribe("inbox/newMessage", messageLogger);

let subscription1 = pubsub.subscribe("inbox/newMessage", messageLogger);

let subscription2 = pubsub.subscribe("inbox/newMessage", messageLogger);

pubsub.publish("inbox/newMessage", "hello world!");